/**
 * 精确度保留 Vector2
 * 
 * @description
 * 使用精确度四舍五入的 2D 向量
 * 兼容原始 Vector2 接口
 */

import { PrecisionNumber } from './PrecisionNumber';
import { Vector2 } from '../rvo/Common';

/**
 * 精确度保留 Vector2 类
 */
export class PrecisionVector2 {
    
    public x: PrecisionNumber;
    public y: PrecisionNumber;
    
    constructor(x: number | PrecisionNumber = 0, y: number | PrecisionNumber = 0) {
        if (typeof x === 'number') {
            this.x = new PrecisionNumber(x);
            this.y = new PrecisionNumber(y as number);
        } else {
            this.x = x;
            this.y = y as PrecisionNumber;
        }
    }
    
    /**
     * 从普通 Vector2 创建
     */
    static fromVector2(v: Vector2 | { x: number; y: number }): PrecisionVector2 {
        return new PrecisionVector2(v.x, v.y);
    }
    
    /**
     * 转换为普通 Vector2
     */
    toVector2(): Vector2 {
        return new Vector2(this.x.value, this.y.value);
    }
    
    /**
     * 向量加法
     */
    plus(other: PrecisionVector2): PrecisionVector2 {
        return new PrecisionVector2(
            this.x.add(other.x),
            this.y.add(other.y)
        );
    }
    
    /**
     * 向量加法（原地修改）
     */
    plusInPlace(other: PrecisionVector2): this {
        this.x = this.x.add(other.x);
        this.y = this.y.add(other.y);
        return this;
    }
    
    /**
     * 向量减法
     */
    minus(other: PrecisionVector2): PrecisionVector2 {
        return new PrecisionVector2(
            this.x.sub(other.x),
            this.y.sub(other.y)
        );
    }
    
    /**
     * 向量减法（原地修改）
     */
    minusInPlace(other: PrecisionVector2): this {
        this.x = this.x.sub(other.x);
        this.y = this.y.sub(other.y);
        return this;
    }
    
    /**
     * 点积
     */
    multiply(other: PrecisionVector2): PrecisionNumber {
        return this.x.mul(other.x).add(this.y.mul(other.y));
    }
    
    /**
     * 缩放
     */
    scale(factor: number | PrecisionNumber): PrecisionVector2 {
        const f = typeof factor === 'number' ? new PrecisionNumber(factor) : factor;
        return new PrecisionVector2(
            this.x.mul(f),
            this.y.mul(f)
        );
    }
    
    /**
     * 缩放（原地修改）
     */
    scaleInPlace(factor: number | PrecisionNumber): this {
        const f = typeof factor === 'number' ? new PrecisionNumber(factor) : factor;
        this.x = this.x.mul(f);
        this.y = this.y.mul(f);
        return this;
    }
    
    /**
     * 复制
     */
    copy(v: PrecisionVector2 | { x: number; y: number }): this {
        if (v instanceof PrecisionVector2) {
            this.x = v.x.clone();
            this.y = v.y.clone();
        } else {
            this.x = new PrecisionNumber(v.x);
            this.y = new PrecisionNumber(v.y);
        }
        return this;
    }
    
    /**
     * 克隆
     */
    clone(): PrecisionVector2 {
        return new PrecisionVector2(this.x.clone(), this.y.clone());
    }
    
    /**
     * 长度的平方
     */
    lengthSqr(): PrecisionNumber {
        return this.x.square().add(this.y.square());
    }
    
    /**
     * 长度
     */
    length(): PrecisionNumber {
        return this.lengthSqr().sqrt();
    }
    
    /**
     * 归一化
     */
    normalize(): PrecisionVector2 {
        const len = this.length();
        if (len.value === 0) {
            return new PrecisionVector2(0, 0);
        }
        return new PrecisionVector2(
            this.x.div(len),
            this.y.div(len)
        );
    }
    
    /**
     * 归一化（原地修改）
     */
    normalizeInPlace(): this {
        const len = this.length();
        if (len.value > 0) {
            this.x = this.x.div(len);
            this.y = this.y.div(len);
        }
        return this;
    }
    
    /**
     * 设置值
     */
    set(x: number | PrecisionNumber, y: number | PrecisionNumber): this {
        if (typeof x === 'number') {
            this.x = new PrecisionNumber(x);
            this.y = new PrecisionNumber(y as number);
        } else {
            this.x = x;
            this.y = y as PrecisionNumber;
        }
        return this;
    }
    
    /**
     * 重置为零
     */
    reset(): this {
        this.x = PrecisionNumber.ZERO.clone();
        this.y = PrecisionNumber.ZERO.clone();
        return this;
    }
    
    /**
     * 转换为字符串
     */
    toString(): string {
        return `PrecisionVector2(${this.x.toString()}, ${this.y.toString()})`;
    }
    
    /**
     * 静态点积
     */
    static dot(v1: PrecisionVector2, v2: PrecisionVector2): PrecisionNumber {
        return v1.multiply(v2);
    }
    
    /**
     * 叉积
     */
    static det(v1: PrecisionVector2, v2: PrecisionVector2): PrecisionNumber {
        return v1.x.mul(v2.y).sub(v1.y.mul(v2.x));
    }
}

/**
 * 精确度保留 RVOMath
 */
export class PrecisionRVOMath {
    
    static readonly RVO_EPSILON = new PrecisionNumber(0.00001);
    
    static absSq(v: PrecisionVector2): PrecisionNumber {
        return v.lengthSqr();
    }
    
    static abs(v: PrecisionVector2): PrecisionNumber {
        return v.length();
    }
    
    static normalize(v: PrecisionVector2): PrecisionVector2 {
        return v.normalize();
    }
    
    static det(v1: PrecisionVector2, v2: PrecisionVector2): PrecisionNumber {
        return PrecisionVector2.det(v1, v2);
    }
    
    static sqr(x: PrecisionNumber): PrecisionNumber {
        return x.square();
    }
    
    static distSqPointLineSegment(
        v1: PrecisionVector2,
        v2: PrecisionVector2,
        v3: PrecisionVector2
    ): PrecisionNumber {
        const aux1 = v3.minus(v1);
        const aux2 = v2.minus(v1);
        
        const aux2LenSq = aux2.lengthSqr();
        if (aux2LenSq.value === 0) {
            return aux1.lengthSqr();
        }
        
        const r = aux1.multiply(aux2).div(aux2LenSq);
        
        if (r.lt(PrecisionNumber.ZERO)) {
            return aux1.lengthSqr();
        } else if (r.gt(PrecisionNumber.ONE)) {
            return v3.minus(v2).lengthSqr();
        } else {
            const proj = v1.plus(aux2.scale(r));
            return v3.minus(proj).lengthSqr();
        }
    }
    
    static leftOf(a: PrecisionVector2, b: PrecisionVector2, c: PrecisionVector2): PrecisionNumber {
        return PrecisionRVOMath.det(a.minus(c), b.minus(a));
    }
}

